package com.piece.core.web.interceptor;

import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.util.collection.ArrayUtil;
import com.piece.core.framework.util.net.URLUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.log.annotation.OperationType;
import com.piece.core.log.client.dto.AuditLogDTO;
import com.piece.core.log.client.LogFactory;
import com.piece.core.log.client.dto.VisitInfoDTO;
import com.piece.core.log.config.RequestContextHolder;
import com.piece.core.log.config.TenantContextHolder;
import com.piece.core.log.config.UserContextHolder;
import com.piece.core.log.enums.AuditLogType;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.async.AsyncManager;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.log.support.LogUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StopWatch;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.TimerTask;

/**
 * 审计日志拦截器
 */
@Slf4j
public class StreamDataAuditInterceptor extends InterceptorHookSupport {

    private final String ATTRIBUTE = "system_stop_watch";

    @Value("${spring.application.name:#{null}}")
    private String applicationName;

    @Autowired
    private ApplicationProperties applicationProperties;

    @Autowired
    private LogFactory auditLogFactory;

    @Override
    public String[] getInterceptor() {
        return null;
    }

    @Override
    public void preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
        if (Convert.toBool(applicationProperties.getValue(ApplicationConstants.SYSTEM_AUDIT_LOG_ENABLE), false)) {
            Method method = handler.getMethod();
            OperationType operationType = method.getAnnotation(OperationType.class);
            if (null != operationType) {
                StopWatch stopWatch = new StopWatch(System.currentTimeMillis() + "");
                stopWatch.start();
                request.setAttribute(ATTRIBUTE, stopWatch);
            }
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler, Exception ex) {
        StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE);
        if (null != stopWatch && stopWatch.isRunning()) {
            stopWatch.stop();
            long seconds = stopWatch.getTotalTimeMillis();
            Method method = handler.getMethod();
            OperationType operationType = method.getAnnotation(OperationType.class);
            Map param = (Map) ObjectUtil.clone(request.getParameterMap());

            AsyncManager.getInstance().submit(new TimerTask() {
                @Override
                public void run() {
                    RequestContextHolder.set(request, request.getHeader("user-agent"));
                    AuditLogDTO auditLog = LogUtil.buildAuditLog(applicationName);
                    // 记录应用信息
                    String module = "";
                    Class controller = handler.getBeanType();
                    Api api = (Api) controller.getAnnotation(Api.class);
                    if (null != api) {
                        module += ArrayUtil.join(api.tags(), ",") + ": ";
                    }

                    ApiOperation operation = method.getAnnotation(ApiOperation.class);
                    if (null != operation) {
                        module += operation.value();
                    }
                    auditLog.setModuleName(module);
                    if (null != operationType) {
                        auditLog.setBusinessType(operationType.value().getType() + "");
                    } else {
                        auditLog.setBusinessType(AuditLogType.WATCH.getType() + "");
                    }

                    // 记录方法信息
                    auditLog.setOperaUrl(request.getRequestURI());
                    auditLog.setClassName(handler.getBeanType().getSimpleName());
                    auditLog.setMethodName(method.getName());
                    // 记录操作人员
                    auditLog.setClientId(TenantContextHolder.getTenant());
                    auditLog.setOperaId(UserContextHolder.getUserId());
                    auditLog.setOperaName(UserContextHolder.getUserName());
                    auditLog.setOperaUrl(request.getRequestURI());
                    auditLog.setOperatorType("00");
                    // 记录访问来源信息
                    VisitInfoDTO visitInfo = RequestContextHolder.get();
                    auditLog.setIp(visitInfo.getIp());
                    auditLog.setArea(visitInfo.getArea());
                    auditLog.setCity(visitInfo.getCity());
                    auditLog.setBrowser(visitInfo.getBrowser());
                    auditLog.setOs(visitInfo.getOs());
                    // 记录请求和异常信息
                    auditLog.setOperaParam(URLUtil.appendQueryFromRequest(param));
                    if (null != ex) {
                        auditLog.setStatus(StatusType.FAIL.getCode());
                        auditLog.setDesc(StringUtils.substring(ex.getMessage(), 0, 2000));
                    } else {
                        auditLog.setStatus(StatusType.SUCCESS.getCode());
                    }
                    // 记录时间信息
                    auditLog.setDesc("方法耗时：" + seconds + "毫秒");

                    auditLogFactory.getAuditLog().save(auditLog);
                }
            });

            if (log.isDebugEnabled()) {
                log.debug("[" + method.getName() + "]方法耗时：" + seconds + "毫秒");
            }
        }
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) {
    }
}
